package com.lcf.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.lcf.framework.exception.SysException;
import com.lcf.framework.utils.UserUtils;
import com.lcf.framework.vo.PageVO;
import com.lcf.system.dao.UserOrderDao;
import com.lcf.system.dto.OrderGoodsDTO;
import com.lcf.system.dto.UserOrderDTO;
import com.lcf.system.entity.Goods;
import com.lcf.system.entity.OrderGoods;
import com.lcf.system.entity.UserOrder;
import com.lcf.system.entity.UserScore;
import com.lcf.system.enums.OrderStatusEnum;
import com.lcf.system.query.UserOrderPageQuery;
import com.lcf.system.service.UserOrderService;
import com.lcf.system.vo.OrderGoodsVO;
import com.lcf.system.vo.UserOrderVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static java.util.stream.Collectors.toMap;

/**
 * 订单(UserOrder)表服务实现类
 *
 * @author lcf
 * @since 2025-04-24 21:14:33
 */
@Service("userOrderService")
public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> implements UserOrderService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insert(UserOrderDTO dto) {
        UserOrder userOrder = new UserOrder();
        userOrder.setOrderNo(RandomUtil.randomNumbers(32));
        userOrder.setOpenId(UserUtils.getOpenId());
        userOrder.setOrderTime(LocalDateTime.now());
        userOrder.setOrderStatus(OrderStatusEnum.WAIT_PAY.getValue());
        userOrder.insert();

        List<Long> goodsIdList =  dto.getGoodsList().stream().map(OrderGoodsDTO::getGoodsId).toList();
        List<Goods> goodsList = Db.lambdaQuery(Goods.class).in(Goods::getId, goodsIdList).list();
        if (CollectionUtils.isEmpty(goodsList)
                || goodsList.stream().anyMatch(x -> goodsIdList.stream().noneMatch(goodsId -> goodsId.equals(x.getId())))) {
            throw new SysException("商品不存在");
        }

        List<OrderGoods> list = dto.getGoodsList().stream().map(x -> {
            OrderGoods orderGoods = BeanUtil.copyProperties(x, OrderGoods.class);
            orderGoods.setOrderId(userOrder.getId());
            orderGoods.setOrderNo(userOrder.getOrderNo());
            goodsList.stream().filter(goods -> goods.getId().equals(x.getGoodsId())).findAny()
                    .ifPresent(goods -> orderGoods.setGoodsPrice(goods.getGoodsPrice()));
            return orderGoods;
        }).toList();

        return Db.saveBatch(list);
    }

    @Override
    public boolean pay(Long orderId) {
        boolean update = Db.lambdaUpdate(UserOrder.class)
                .set(UserOrder::getOrderStatus, OrderStatusEnum.WAIT_DELIVER.getValue())
                .eq(UserOrder::getId, orderId)
                .eq(UserOrder::getOrderStatus, OrderStatusEnum.WAIT_PAY.getValue())
                .update();

        if (!update) {
            return false;
        }

        BigDecimal totalPrice = Db.lambdaQuery(OrderGoods.class).eq(OrderGoods::getOrderId, orderId).list()
                .stream().map(x -> x.getGoodsPrice().multiply(new BigDecimal(x.getGoodsNum())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        UserScore userScore = Db.lambdaQuery(UserScore.class).eq(UserScore::getOpenId, UserUtils.getOpenId()).one();
        if (userScore != null) {
            Db.lambdaUpdate(UserScore.class)
                    .eq(UserScore::getOpenId, UserUtils.getOpenId())
                    .set(UserScore::getScore, totalPrice.intValue() + userScore.getScore())
                    .update();
        }

        return true;
    }

    @Override
    public boolean deliver(Long orderId) {
        return Db.lambdaUpdate(UserOrder.class)
                .set(UserOrder::getOrderStatus, OrderStatusEnum.WAIT_RECEIVE.getValue())
                .eq(UserOrder::getId, orderId)
                .eq(UserOrder::getOrderStatus, OrderStatusEnum.WAIT_DELIVER.getValue())
                .update();
    }

    @Override
    public boolean confirm(Long orderId) {
        return Db.lambdaUpdate(UserOrder.class)
                .set(UserOrder::getOrderStatus, OrderStatusEnum.WAIT_EVALUATE.getValue())
                .eq(UserOrder::getId, orderId)
                .eq(UserOrder::getOrderStatus, OrderStatusEnum.WAIT_RECEIVE.getValue())
                .update();
    }

    @Override
    public boolean refund(Long orderId) {
        return Db.lambdaUpdate(UserOrder.class)
                .set(UserOrder::getOrderStatus, OrderStatusEnum.REFUNDED.getValue())
                .eq(UserOrder::getId, orderId)
                .update();
    }

    @Override
    public PageVO<UserOrderVO> page(UserOrderPageQuery query) {
        Page<UserOrder> page = Db.lambdaQuery(UserOrder.class)
                .eq(UserOrder::getOpenId, UserUtils.getOpenId())
                .eq(query.getOrderNo() == null, UserOrder::getOrderNo, query.getOrderNo())
                .eq(query.getOrderStatus() != null, UserOrder::getOrderStatus, query.getOrderStatus())
                .orderByDesc(UserOrder::getOrderTime)
                .page(query.toPage());
        if (page == null || page.getTotal() == 0) {
            return new PageVO<>(page, UserOrderVO.class);
        }

        List<Long> orderIdList = page.getRecords().stream().map(UserOrder::getId).toList();
        List<OrderGoods> orderGoodsList = Db.lambdaQuery(OrderGoods.class).in(OrderGoods::getOrderId, orderIdList).list();
        if (CollectionUtils.isEmpty(orderGoodsList)) {
            return new PageVO<>(page, UserOrderVO.class);
        }

        List<Long> goodsIdList = orderGoodsList.stream().map(OrderGoods::getGoodsId).toList();
        List<Goods> list = Db.lambdaQuery(Goods.class).in(Goods::getId, goodsIdList).list();
        Map<Long, Goods> goodsMap = list.stream().collect(toMap(Goods::getId, Function.identity()));

        return new PageVO<>(page, x -> {
            UserOrderVO vo = BeanUtil.copyProperties(x, UserOrderVO.class);

            List<OrderGoods> orderGoods = orderGoodsList.stream().filter(g -> g.getOrderId().equals(x.getId())).toList();
            List<OrderGoodsVO> orderGoodsVoList = BeanUtil.copyToList(orderGoods, OrderGoodsVO.class);

            orderGoodsVoList.forEach(orderGoodsVo -> {
                Goods goods = goodsMap.get(orderGoodsVo.getGoodsId());
                orderGoodsVo.setGoodsName(goods.getGoodsName());
                orderGoodsVo.setGoodsImg(goods.getGoodsImg());
            });

            vo.setOrderGoods(orderGoodsVoList);

            return vo;
        });
    }
    @Override
    public PageVO<UserOrderVO> pageAll(UserOrderPageQuery query) {
        Page<UserOrder> page = Db.lambdaQuery(UserOrder.class)
                .eq(query.getOrderStatus() != null, UserOrder::getOrderStatus, query.getOrderStatus())
                .orderByDesc(UserOrder::getOrderTime)
                .page(query.toPage());
        if (page == null || page.getTotal() == 0) {
            return new PageVO<>(page, UserOrderVO.class);
        }

        List<Long> orderIdList = page.getRecords().stream().map(UserOrder::getId).toList();
        List<OrderGoods> orderGoodsList = Db.lambdaQuery(OrderGoods.class).in(OrderGoods::getOrderId, orderIdList).list();
        if (CollectionUtils.isEmpty(orderGoodsList)) {
            return new PageVO<>(page, UserOrderVO.class);
        }

        List<Long> goodsIdList = orderGoodsList.stream().map(OrderGoods::getGoodsId).toList();
        List<Goods> list = Db.lambdaQuery(Goods.class).in(Goods::getId, goodsIdList).list();
        Map<Long, Goods> goodsMap = list.stream().collect(toMap(Goods::getId, Function.identity()));

        return new PageVO<>(page, x -> {
            UserOrderVO vo = BeanUtil.copyProperties(x, UserOrderVO.class);

            List<OrderGoods> orderGoods = orderGoodsList.stream().filter(g -> g.getOrderId().equals(x.getId())).toList();
            List<OrderGoodsVO> orderGoodsVoList = BeanUtil.copyToList(orderGoods, OrderGoodsVO.class);

            orderGoodsVoList.forEach(orderGoodsVo -> {
                Goods goods = goodsMap.get(orderGoodsVo.getGoodsId());
                orderGoodsVo.setGoodsName(goods.getGoodsName());
                orderGoodsVo.setGoodsImg(goods.getGoodsImg());
            });

            vo.setOrderGoods(orderGoodsVoList);

            return vo;
        });
    }
    @Override
    public UserOrderVO detail(long id) {
        UserOrder one = Db.lambdaQuery(UserOrder.class).eq(UserOrder::getId, id).one();
        if (one == null) {
            throw new SysException("订单不存在");
        }

        List<OrderGoods> orderGoodsList = Db.lambdaQuery(OrderGoods.class).eq(OrderGoods::getOrderId, id).list();
        if (CollectionUtils.isEmpty(orderGoodsList)) {
            return BeanUtil.copyProperties(one, UserOrderVO.class);
        }

        UserOrderVO order = BeanUtil.copyProperties(one, UserOrderVO.class);

        List<Long> goodsIdList = orderGoodsList.stream().map(OrderGoods::getGoodsId).toList();
        List<Goods> list = Db.lambdaQuery(Goods.class).in(Goods::getId, goodsIdList).list();
        Map<Long, Goods> goodsMap = list.stream().collect(toMap(Goods::getId, Function.identity()));

        List<OrderGoodsVO> orderGoods = BeanUtil.copyToList(orderGoodsList, OrderGoodsVO.class);

        orderGoods.forEach(orderGoodsVo -> {
            Goods goods = goodsMap.get(orderGoodsVo.getGoodsId());
            orderGoodsVo.setGoodsName(goods.getGoodsName());
            orderGoodsVo.setGoodsImg(goods.getGoodsImg());
        });

        order.setOrderGoods(orderGoods);

        return order;
    }
}

